﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Linq;



namespace LunchRoller
{

    public class TryExecuteResult<TContext, TResult>
    {

        public TryExecuteResult(TContext context)
            : this(context, default(TResult), null)
        {

        }

        public TryExecuteResult(TContext context, Exception exception)
            : this(context, default(TResult), exception)
        {

        }

        public TryExecuteResult(TContext context, TResult result)
            : this(context, result, null)
        {


        }

        public TryExecuteResult(TContext context, TResult result, Exception exception)
        {
            TryContext = context;
            TryResult = result;
            Exception = exception;
            HasException = (exception == null);

        }


        public bool HasException { get; private set; }

        public Exception Exception { get; private set; }

        public TContext TryContext { get; private set; }
        public TResult TryResult { get; private set; }
    }


    public class TryDoResult<TContext>
    {

        public TryDoResult(TContext context)
            : this(context, null)
        {

        }



        public TryDoResult(TContext context, Exception exception)
        {
            TryContext = context;

            Exception = exception;
            HasException = (exception == null);

        }


        public bool HasException { get; private set; }

        public Exception Exception { get; private set; }

        public TContext TryContext { get; private set; }

    }
    static public class ObjectExtensions
    {



        public static T Do<T>(this T source, Action<T> action)
        {
            

            action(source);
            return source;



        }

        public static TTarget CastAs<TTarget>(this object source)
        {

            return (TTarget)source;

        }

        public static TryExecuteResult<Object, TTarget> TryCastAs<TTarget>(this object source)
        {

            return source.Try(s => s.CastAs<TTarget>());
        }




        public static IEnumerable<T> LazyForEach<T>(this IEnumerable<T> sources, Action<T> action)
        {

            return sources.Select(itm => itm.Do(action));
        }
        public static List<T> ForEach<T>(this IEnumerable<T> sources, Action<T> action)
        {
            return sources.LazyForEach(action).ToList();

        }
        public static IEnumerable<TryDoResult<T>> LazyTryEach<T>(this IEnumerable<T> sources, Action<T> action)
        {

            return sources.Select(itm => itm.Try(action));
        }
        public static List<TryDoResult<T>> TryEach<T>(this IEnumerable<T> sources, Action<T> action)
        {
            return sources.Select(itm => itm.Try(action)).ToList();
        }




        public static TryDoResult<T> Try<T>(this T source, Action<T> action)
        {


            try
            {
                action(source);

                return new TryDoResult<T>(source);
            }
            catch (Exception ex)
            {
                return new TryDoResult<T>(source, ex);
            }





        }


        public static TryExecuteResult<TContext, TResult> Try<TContext, TResult>(this TContext source, Func<TContext, TResult> func)
        {
     

            try
            {
                var rval = func(source);

                return new TryExecuteResult<TContext, TResult>(source, rval);
            }
            catch (Exception ex)
            {
                return new TryExecuteResult<TContext, TResult>(source, ex);
            }


        }


        public static IEnumerable<T> ToSingleEnumerable<T>(this T item)
        {
            yield return item;
        }
        public static IEnumerable<T> SingleUnion<T>(this IEnumerable<T> items, T newItem)
        {
            return items.Union(newItem.ToSingleEnumerable());
        }
    }
}
